package dao

import (
	"context"
	"errors"
	"fmt"
	"time"

	"bmember/pkg/util"

	"github.com/patrickmn/go-cache"

	"bmember/pkg/db/pgex"

	"bmember/internal/model"
)

// CardDao 体系相关 dao 层
type CardDao interface {
	// CreateAccountGroup 创建AccountGroup
	CreateAccountGroup(ctx context.Context, g *model.AccountGroup) (int64, error)
	// GetAccountGroupByBrandId 按品牌获取体系
	GetAccountGroupByBrandId(ctx context.Context, brandId int64) ([]*model.AccountGroup, error)
	// CreateAccountCard 创建卡
	CreateAccountCard(ctx context.Context, c *model.AccountCard) (int64, error)
	// GetAccountCardById 根据 id 获取卡信息
	GetAccountCardById(ctx context.Context, cardId int64) (*model.AccountCard, error)
	// LockAccountCardById 根据 id 锁定卡信息
	LockAccountCardById(ctx context.Context, cardId int64) (*model.AccountCard, error)
	// GetAccountGroupById 根据 id 获取 group
	GetAccountGroupById(ctx context.Context, groupId int64) (*model.AccountGroup, error)
	// GetCardAggByCardId 获取卡聚合信息
	GetCardAggByCardId(ctx context.Context, cardId int64) (*model.CardAgg, error)
	// UpdateAccountCard 更新卡信息
	UpdateAccountCard(ctx context.Context, card *model.AccountCard) error
	// GetAccountCardsByGroup 获取卡定义列表
	GetAccountCardsByGroup(ctx context.Context, brandId, groupId int64) ([]*model.AccountCard, error)
	// GetAccountCardsNameByBrand 获取卡名列表
	GetAccountCardsNameByBrand(ctx context.Context, brandId int64) ([]*model.AccountCard, error)
	// DisableDefaultCard 关闭体系下默认卡
	DisableDefaultCard(ctx context.Context, brandId, groupId, editorId int64) error
	// SetDefaultCard 设置默认卡
	SetDefaultCard(ctx context.Context, cardId int64, isDefault bool, editorId int64) error
	// GetDefaultCard 获取体系下默认卡
	GetDefaultCard(ctx context.Context, groupId int64) (*model.AccountCard, error)
}

type cDaoImpl struct {
	*DaoBase
	name string
}

var gDao CardDao

// InitCardDao 初始化 体系配置 dao 层
func InitCardDao() {
	gDao = &cDaoImpl{
		DaoBase: daoBase,
		name:    "card_dao",
	}
}

func GetCardDao() CardDao {
	return gDao
}

// CreateAccountGroup 创建体系
func (dao *cDaoImpl) CreateAccountGroup(ctx context.Context, g *model.AccountGroup) (int64, error) {
	if g == nil {
		return 0, nil
	}
	param := []interface{}{
		g.BrandId,
		g.Name,
		g.IsAllStores,
		g.StoreIds,
		g.EditorId,
		g.CreatedAt,
		g.UpdatedAt,
		g.DeletedAt,
	}
	query := "INSERT INTO account_group (brand_id, name, is_all_stores, store_ids, editor_id, created_at, updated_at, deleted_at) " +
		"VALUES (?,?,?,?,?,?,?,?)"
	aff, err := dao.db.PgExec(ctx, "id", query, param...)
	if err != nil {
		return 0, err
	}
	lastId, _ := aff.GetLastId()
	return lastId, nil
}

var groupCache = cache.New(5*time.Minute, 10*time.Minute)

// GetAccountGroupByBrandId 品牌获取体系
func (dao *cDaoImpl) GetAccountGroupByBrandId(ctx context.Context, brandId int64) ([]*model.AccountGroup, error) {
	key := fmt.Sprintf("brand:%d", brandId)
	if card, found := cardCache.Get(key); found {
		return card.([]*model.AccountGroup), nil
	}
	var groups []*model.AccountGroup
	query := "SELECT * FROM account_group WHERE brand_id=? AND deleted_at=0"
	err := dao.db.Query(ctx, &groups, query, brandId)
	if err != nil {
		return nil, err
	}
	groupCache.SetDefault(key, groups)
	return groups, err
}

// CreateAccountCard 创建卡
func (dao *cDaoImpl) CreateAccountCard(ctx context.Context, c *model.AccountCard) (int64, error) {
	if c == nil {
		return 0, nil
	}
	param := []interface{}{
		c.BrandId,
		c.GroupId,
		c.Name,
		c.CardType,
		c.Cover,
		c.IsDefault,
		c.CanIntegral,
		c.CanRecharge,
		c.Rule,
		c.Status,
		c.Deposit,
		c.EditorId,
		c.CreatedAt,
		c.UpdatedAt,
		c.DeletedAt,
	}
	query := "INSERT INTO account_card (brand_id, group_id, name, card_type, cover, is_default, can_integral, can_recharge, " +
		"rule, status, deposit, editor_id, created_at, updated_at, deleted_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
	aff, err := dao.db.PgExec(ctx, "id", query, param...)
	if err != nil {
		return 0, err
	}
	lastId, _ := aff.GetLastId()
	return lastId, nil
}

// GetAccountCardById 根据 id 获取卡信息
func (dao *cDaoImpl) GetAccountCardById(ctx context.Context, cardId int64) (*model.AccountCard, error) {
	card := &model.AccountCard{}
	query := "SELECT * FROM account_card WHERE id=? AND deleted_at=0"
	err := dao.db.Get(ctx, card, query, cardId)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return card, err
}

// LockAccountCardById 根据 id 锁定卡信息
func (dao *cDaoImpl) LockAccountCardById(ctx context.Context, cardId int64) (*model.AccountCard, error) {
	card := &model.AccountCard{}
	query := "SELECT * FROM account_card WHERE id=? AND deleted_at=0 FOR UPDATE"
	err := dao.db.Get(ctx, card, query, cardId)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return card, err
}

// GetAccountGroupById 根据 id 获取 group
func (dao *cDaoImpl) GetAccountGroupById(ctx context.Context, groupId int64) (*model.AccountGroup, error) {
	query := "SELECT * FROM account_group WHERE id=? AND deleted_at=0"
	group := &model.AccountGroup{}
	err := dao.db.Get(ctx, group, query, groupId)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return group, err
}

var cardCache = cache.New(5*time.Minute, 10*time.Minute)

// GetCardAggByCardId 获取卡聚合信息
func (dao *cDaoImpl) GetCardAggByCardId(ctx context.Context, cardId int64) (*model.CardAgg, error) {
	key := fmt.Sprintf("card:%d", cardId)
	if card, found := cardCache.Get(key); found {
		return card.(*model.CardAgg), nil
	}
	card, err := dao.GetAccountCardById(ctx, cardId)
	if err != nil {
		return nil, err
	}
	if card == nil {
		return nil, nil
	}
	group, err := dao.GetAccountGroupById(ctx, card.GroupId)
	if err != nil {
		return nil, err
	}
	if group == nil {
		return nil, nil
	}
	// 塞入进程缓存
	agg := model.NewCardAgg(group, card)
	cardCache.SetDefault(key, agg)
	return agg, nil
}

// UpdateAccountCard 更新卡信息
func (dao *cDaoImpl) UpdateAccountCard(ctx context.Context, card *model.AccountCard) error {
	if card == nil {
		return nil
	}
	param := []interface{}{
		card.Name,
		card.Cover,
		card.CanIntegral,
		card.CanRecharge,
		card.Rule,
		card.Status,
		card.Deposit,
		card.EditorId,
		card.UpdatedAt,
		card.Id,
	}
	query := "UPDATE account_card SET name=?, cover=?, can_integral=?, can_recharge=?, rule=?, status=?, deposit=?, " +
		"editor_id=?, updated_at=? WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, param...)
	return err
}

// GetAccountCardsByGroup 获取卡定义列表
func (dao *cDaoImpl) GetAccountCardsByGroup(ctx context.Context, brandId, groupId int64) ([]*model.AccountCard, error) {
	query := "SELECT id, brand_id, group_id, name, card_type, cover, is_default, can_integral, can_recharge, rule, status FROM " +
		"account_card WHERE brand_id=? AND group_id=? AND deleted_at=0 ORDER BY updated_at DESC"
	var cards []*model.AccountCard
	err := dao.db.Query(ctx, &cards, query, brandId, groupId)
	return cards, err
}

// GetAccountCardsNameByBrand 获取卡名列表
func (dao *cDaoImpl) GetAccountCardsNameByBrand(ctx context.Context, brandId int64) ([]*model.AccountCard, error) {
	query := "SELECT id, group_id, name FROM account_card WHERE brand_id=? AND deleted_at=0"
	var cards []*model.AccountCard
	err := dao.db.Query(ctx, &cards, query, brandId)
	return cards, err
}

// DisableDefaultCard 关闭体系下默认卡
func (dao *cDaoImpl) DisableDefaultCard(ctx context.Context, brandId, groupId, editorId int64) error {
	query := "UPDATE account_card SET is_default=false, updated_at=?, editor_id=? WHERE brand_id=? AND group_id=? AND is_default=true AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, util.GetNow(), editorId, brandId, groupId)
	return err
}

// SetDefaultCard 设置默认卡
func (dao *cDaoImpl) SetDefaultCard(ctx context.Context, cardId int64, isDefault bool, editorId int64) error {
	query := "UPDATE account_card SET is_default=?, updated_at=?, editor_id=? WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, isDefault, util.GetNow(), editorId, cardId)
	return err
}

// GetDefaultCard 获取体系下默认卡
func (dao *cDaoImpl) GetDefaultCard(ctx context.Context, groupId int64) (*model.AccountCard, error) {
	var cards []*model.AccountCard
	query := "SELECT * FROM account_card WHERE group_id=? AND is_default=true AND deleted_at=0"
	err := dao.db.Query(ctx, &cards, query, groupId)
	if err != nil {
		return nil, err
	}
	if len(cards) == 0 {
		return nil, nil
	}
	return cards[0], nil
}
